//*********************************************************************
//		Copyright (c) 2003 - 2008 Microsoft Corporation.
//      This code is provided under Microsoft Public License, which can be found at
//      http://www.microsoft.com/resources/sharedsource/licensingbasics/publiclicense.mspx 
//
// @File: Configurator.cs
//
// Purpose: processes config file
//
//
//
// Notes: 
//
//
//	
// History: 
//     @Version: V2.1
//
//     10/31/08 PGV 2.1.001.0 Starting V2.1 (based on 2.0.002.0 code)
//
//
// @EndHeader@
//*********************************************************************

using System;
using System.Collections;
using System.Xml;
using System.IO;
using System.Diagnostics;
using Microsoft.Win32;

namespace PerfService
{
    [Serializable]
    public class ConfiguratorException : Exception
    {
        public ConfiguratorException () : base () { }
        public ConfiguratorException (string message) : base (message) { }
        public ConfiguratorException (string message, Exception exception) : base (message, exception) { }
        protected ConfiguratorException (System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
            : base (info, context) { }
    }

	public sealed class Configurator
	{
		public static SortedList Computers;
		public static SortedList Counters;

        private Configurator () { }

		static Configurator ()
		{
			Computers = new SortedList ();
			Counters = new SortedList ();
		}

		public static void ReadConfig (PerfMgr pm, string iniFileName)
		{
			XmlElement root;

			ReadSettings (iniFileName, out root);
			ValidateSettings ();
			ReadTargets (pm, root);
		}

		static void ReadSettings (string iniFileName, out XmlElement root)
		{
			string svalue;
			XmlDocument xd;
			XmlNode xn;
			XmlNodeList tList;

			xd = new XmlDocument ();
			xd.Load (iniFileName);
			root = xd.DocumentElement;

			xn = root.SelectSingleNode("/ROOT/SETTINGS"); 
			tList = xn.ChildNodes;

			foreach (XmlNode tnode in tList)
			{
				switch (tnode.NodeType)
				{
					case XmlNodeType.Element:
						svalue = tnode.SelectSingleNode("attribute::Value").Value;
						SQLH2PerfCollector.Log ("Node: "+tnode.Name+", attribute: "+svalue, EventLogEntryType.Information, 99);
					switch (tnode.Name)
					{
						case "CollectionIntervalSeconds": 
							Constants.CollectionIntervalSeconds = Convert.ToInt32(svalue);
							break;
						case "RetryIntervalSeconds":
							Constants.RetryIntervalSeconds =  Convert.ToInt32(svalue);
							break;
						case "RecorderSleepTimeSeconds":
							Constants.RecorderSleepTimeSeconds = Convert.ToInt32(svalue);
							break;
						case "MaxFileSizeMB":
							Constants.MaxFileSize = 1024*1024*Convert.ToInt32(svalue);
							break;
						case "VerboseLogging":
							if (svalue.ToLower () == "true") Constants.VerboseLogging = true;
							break;
						case "UseInvariantCulture":
							if (svalue.ToLower () == "true") Constants.UseInvariantCulture = true;
							break;
						default:
							throw new ConfiguratorException (Constants.ConfigFileName+": Unknown node "+tnode.Name);
					}
						break;
					case XmlNodeType.Comment:
						continue;
					default:
						throw new ConfiguratorException (Constants.ConfigFileName+": Unexpected node type "+tnode.NodeType.ToString ());
				}
			}

			ValidateSettings ();
		}

		static void ValidateSettings ()
		{
			if (Constants.CollectionIntervalSeconds > Constants.MaxCollectionInterval)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": CollectionInterval exceeds maximum allowed."+Environment.NewLine
					+"CollectionInterval is set to Maximum.", EventLogEntryType.Information, 201);
				Constants.CollectionIntervalSeconds = Constants.MaxCollectionInterval;
			}
			else if (Constants.CollectionIntervalSeconds < Constants.MinCollectionInterval)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": CollectionInterval is less than minimum allowed."+Environment.NewLine
					+"CollectionInterval is set to Minimum.", EventLogEntryType.Information, 202);
				Constants.CollectionIntervalSeconds = Constants.MinCollectionInterval;
			}

			if (Constants.RetryIntervalSeconds > Constants.MaxRetryInterval)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": RetryInterval exceeds maximum allowed."+Environment.NewLine
					+"RetryInterval is set to Maximum.", EventLogEntryType.Information, 203);
				Constants.RetryIntervalSeconds = Constants.MaxRetryInterval;
			}
			else if (Constants.RetryIntervalSeconds < Constants.MinRetryInterval)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": RetryInterval is less than minimum allowed."+Environment.NewLine
					+"RetryInterval is set to Minimum.", EventLogEntryType.Information, 204);
				Constants.RetryIntervalSeconds = Constants.MinRetryInterval;
			}

			if (Constants.MaxFileSize > Constants.AbsoluteMaxFileSize)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": MaxFileSize exceeds maximum allowed."+Environment.NewLine
					+"MaxFileSize is set to Maximum.", EventLogEntryType.Information, 205);
				Constants.MaxFileSize = Constants.AbsoluteMaxFileSize;
			}
			else if (Constants.MaxFileSize < Constants.MinFileSize)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": MaxFileSize is less than minimum allowed."+Environment.NewLine
					+"MaxFileSize is set to Minimum.", EventLogEntryType.Information, 206);
				Constants.MaxFileSize = Constants.MinFileSize;
			}

			if (Constants.RecorderSleepTimeSeconds > Constants.MaxRecorderSleepTime)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": RecorderSleepTime exceeds maximum allowed."+Environment.NewLine
					+"RecorderSleepTime is set to Maximum.", EventLogEntryType.Information, 207);
				Constants.RecorderSleepTimeSeconds = Constants.MaxRecorderSleepTime;
			}
			else if (Constants.MaxFileSize < Constants.MinRecorderSleepTime)
			{
				SQLH2PerfCollector.Log (Constants.ConfigFileName+": RecorderSleepTime is less than minimum allowed."+Environment.NewLine
					+"RecorderSleepTime is set to Minimum.", EventLogEntryType.Information, 208);
				Constants.RecorderSleepTimeSeconds = Constants.MinRecorderSleepTime;
			}
		}

		static void ReadTargets (PerfMgr pm, XmlElement root)
		{
			int curComputerId=0,
				curCounterId=0,
				computerId=0,
				counterId=0,
				computerIdx,
				idx;
			string name;
			XmlNodeList tList,
				cList;
			CounterIdentity identity;
			Collector col;

			tList = root.SelectNodes("/ROOT/TARGET_LIST/TARGET"); 
			if (tList.Count > Constants.MaxNumberOfTargets) 
			{
				throw new Exception ("Number of targets ("+tList.Count+") is too big");
			}

			foreach (XmlNode tnode in tList)
			{
				name = tnode.SelectSingleNode("attribute::Name").Value.ToUpper ();
				if (name == ".")
				{
					name = Environment.MachineName.ToUpper ();
				}

				computerIdx = Computers.IndexOfValue (name);
				if (computerIdx != -1) 
				{
					SQLH2PerfCollector.Log (Constants.ConfigFileName+": duplicate target "+name
						+Environment.NewLine+"Ingoring the target.", EventLogEntryType.Error, 251);
					continue;
				}
				computerId = curComputerId+1;

				col = new Collector (name, computerId);

				cList = tnode.SelectNodes("PERFCOUNTER");
				if (cList.Count > Constants.MaxCountersPerTarget) 
				{
					throw new Exception ("Specified number of counters ("+cList.Count+") for "+name+" exceeds maximum allowed");
				}
				SQLH2PerfCollector.Log (cList.Count+" counters specified for "+name, EventLogEntryType.Information, 98);

				try 
				{
					foreach (XmlNode cnode in cList)
					{
                        identity = new CounterIdentity ();
						identity.Object = cnode.SelectSingleNode("attribute::Object").Value.ToUpper ();
						identity.Counter = cnode.SelectSingleNode("attribute::Counter").Value.ToUpper ();
						try
						{
							identity.Instance = cnode.SelectSingleNode("attribute::Instance").Value.ToUpper ();
						}
						catch (NullReferenceException)
						{
							identity.Instance = "";
						}

						idx = Counters.IndexOfValue (identity);
						try 
						{
							if (idx == -1) 
							{
								col.AddCounter (identity, curCounterId+1);
								counterId = ++curCounterId;
								Counters.Add (counterId, identity);
							}
							else 
							{
								counterId = (int)Counters.GetKey (idx);
								col.AddCounter (identity, counterId);
							}
						}
						catch (InvalidOperationException ex) 
						{
							SQLH2PerfCollector.Log ("Can't initialize counter "+identity.Object+":"+identity.Counter+":"+identity.Instance
								+Environment.NewLine+ex.Message, EventLogEntryType.Error, 31);
						} 

					}

					col.FixSize ();
					pm.AddCollector (col);
					if (computerIdx == -1) Computers.Add (computerId, name);
					++curComputerId;
				}
				catch (Exception ex) 
				{
					SQLH2PerfCollector.Log ("Can't initialize counter on "+name
						+Environment.NewLine+ex.Message, EventLogEntryType.Error, 32);
				} 
			}

			Computers.TrimToSize ();
			Counters.TrimToSize ();

		}

		public static void WriteArrays (StreamWriter stream)
		{
			int i;
			string line;
			CounterIdentity stc;

			stream.WriteLine ("<COMPUTERS>,,,,");
			for (i=0; i<Computers.Count; i++)
			{
				line = Computers.GetKey (i).ToString ()+","+Computers.GetByIndex (i).ToString ()+",,,";
				stream.WriteLine (line);
			}
			stream.WriteLine ("</COMPUTERS>,,,,");

			stream.WriteLine ("<COUNTERS>,,,,");
			for (i=0; i<Counters.Count; i++)
			{
				stc = (CounterIdentity)Counters.GetByIndex (i);
				line = Counters.GetKey (i).ToString ()+","+stc.Object+","+stc.Counter+","+stc.Instance+",";
				stream.WriteLine (line);
			}
			stream.WriteLine ("</COUNTERS>,,,,");
		}
	}
}
		



		